/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.treeeditpart;

import java.util.Iterator;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.parts.ContentOutlinePage;
import org.eclipse.gef.ui.parts.TreeViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.util.TransferDragSourceListener;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.core.IPapyrusUIConstants;
import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.dnd.TreeOutlinePageDragSourceListener;
import com.cea.papyrus.core.dnd.TreeOutlinePageDropTargetListener;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editor.abstracteditor.SelectionSynchronizer;
import com.cea.papyrus.core.presentation.sorters.TypeSorter;
import com.cea.papyrus.core.utils.LookForElement;

/**
 * Creates an outline pagebook for this editor.
 */
public class TreeOutlinePage extends ContentOutlinePage implements ITabbedPropertySheetPageContributor, IAdaptable {	

	/**
	 * 
	 */
	EditDomain editDomain;


	/**
	 * The registry of the editor.
	 */
	ActionRegistry editorActionRegistry;

//	/**
//	* 
//	*/
//	SelectionSynchronizer selectionSynchronizer;

	/**
	 * The model to show.
	 */
	ModelManager modelmanager;
	protected TreeViewer treeviewer=null;
	/**
	 * Tabbed properties page.
	 */
	TabbedPropertySheetPage tabbedPropertySheetPage=null;



	TransferDragSourceListener dragSourceListener = null;
	TransferDropTargetListener dropTargetListener = null;

	/** Key handler */
	protected KeyHandler sharedKeyHandler;

	/**
	 * Create a new outline page for the shapes editor.
	 * 
	 * @param viewer a viewer (TreeViewer instance) used for this outline page
	 * @param editorDomain 
	 * @param model 
	 * @param selectionSynchronizer 
	 * @param actionRegistry 
	 * 
	 * @throws IllegalArgumentException if editor is null
	 */
	public TreeOutlinePage(EditPartViewer viewer, EditDomain editorDomain, ActionRegistry actionRegistry,
			SelectionSynchronizer selectionSynchronizer, ModelManager model) {
		super(viewer);
		this.editDomain = editorDomain;
		editDomain.setActiveTool(new TreeSelectionTool());
		this.editorActionRegistry = actionRegistry;
//		this.selectionSynchronizer = selectionSynchronizer;
		this.modelmanager = model;

	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.ui.parts.ContentOutlinePage#createControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public void createControl(Composite parent) {
		// create outline viewer page
		getViewer().createControl(parent);
		// configure outline viewer
		getViewer().setEditDomain(getEditDomain());
		getViewer().setEditPartFactory(new TreeEditPartFactory(modelmanager));
		// configure & add context menu to viewer
		TreeContextMenuProvider cmProvider = new TreeContextMenuProvider(
				getViewer(), getEditorActionRegistry(),modelmanager); 
		getViewer().setContextMenu(cmProvider);

		// register context menu, so it is aware of extension points org.eclipse.ui.popupmenu
		getSite().registerContextMenu(
				IPapyrusUIConstants.OUTLINE_POPUP_MENU_ID,
				cmProvider, getSite().getSelectionProvider()); 

		// adds the common key handler to outline
		// getViewer().setKeyHandler(getCommonKeyHandler());

		// hook outline viewer
		// getSelectionSynchronizer().addViewer(getViewer());
		// initialize outline viewer with model
		IActionBars actionBars= getSite().getActionBars();
		actionBars.getToolBarManager().add(new SortAction());
		actionBars.getToolBarManager().add(new DiagramViewAction());
		actionBars.getToolBarManager().add(new ShowInheritedElementAction());
		getViewer().setContents(getModel());


		// Initialize selection in the outline
		// Added to correct selection behavior under linux-gtk
		// - when nothing is selected getSelection does not return 
		// - the correct EditPart for drag and drop action
		// 
		ISelection selection = new StructuredSelection(getModel().getModelTreeEditPart());
		getViewer().setSelection(selection);
		//

		treeviewer=(TreeViewer)getViewer();
		// show actions
		// register global actions

		/** DND **/
		setupDragAndDrop();
		/************/
	}


	/**
	 * This method is used to developp the outline in order to see the given uml element
	 * @param elt the uml elemnt that we want to see
	 * @return the found {@link ElementTreeEditPart}
	 */
	public ElementTreeEditPart expandToElement( Element elt){
		TreeViewer tree=(TreeViewer)this.getViewer();
		Iterator<EditPart> iterEditPart=LookForElement.getAllEditParts(getModel().getModelTreeEditPart()).iterator();
		ElementTreeEditPart found=null;
		while (iterEditPart.hasNext()&& found==null) {
			EditPart currentEditPart = iterEditPart.next() ;
			if (currentEditPart.getModel().equals(elt)){
				found=(ElementTreeEditPart)currentEditPart;
			}
		}

		((TreeItem)found.getWidget()).setExpanded(true);
		expand(found);
		return found;

	}

	/**
	 * display in red the given uml Element
	 * It also developps the outline to see the uml element
	 * @param elt the uml element that we want to see
	 */
	public void markInRedElement(Element elt){
		ElementTreeEditPart found =expandToElement(elt);
		((TreeItem)found.getWidget()).setForeground(ColorConstants.red);
	}

	/**
	 * Display in black all item of the outline 
	 */
	public void setDefault(){
		TreeViewer tree=(TreeViewer)this.getViewer();
		Iterator<EditPart> iterEditPart=LookForElement.getAllEditParts(getModel().getModelTreeEditPart()).iterator();
		while (iterEditPart.hasNext()) {
			EditPart editpart=iterEditPart.next() ;
			if(editpart instanceof ElementTreeEditPart){
				ElementTreeEditPart currentEditPart = (ElementTreeEditPart)editpart ;
				((TreeItem)currentEditPart.getWidget()).setForeground(ColorConstants.black);
				((TreeItem)currentEditPart.getWidget()).setBackground(ColorConstants.white);
			}
		}
	}

	private void expand(ElementTreeEditPart et){

		if( et.getParent()!=null){
			if (!(et.getParent() instanceof ModelManagerTreeEditPart)){
				expand((ElementTreeEditPart)et.getParent());}
		}
		((TreeItem)et.getWidget()).setExpanded(true);

	}

	/**
	 * Setting up drag and drop listeners of the TreeOutlinePage.
	 */
	protected void setupDragAndDrop() {
		// setting drag source and drop target for TreeOutlinePage
		dragSourceListener = new TreeOutlinePageDragSourceListener(getViewer());
		dropTargetListener =  new TreeOutlinePageDropTargetListener(getViewer());

		// adding drag and drop target listeners to the viewer
		getViewer().addDropTargetListener(dropTargetListener);
		getViewer().addDragSourceListener(dragSourceListener);
	}





	/* 
	 * Unhook the synchronisation between this viewer and other
	 * viewer.
	 */
	/**
	 * 
	 */
	@Override
	public void dispose() {
		// unhook outline viewer
//		getSelectionSynchronizer().removeViewer(getViewer());

		getViewer().removeDragSourceListener(dragSourceListener);
		getViewer().removeDropTargetListener(dropTargetListener);

		// dispose
		super.dispose();
	}

	/* 
	 * forward to the viewer.
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public Control getControl() {
		return getViewer().getControl();
	}

	/**
	 * Add button and contextual menu.
	 * 
	 * @param pageSite 
	 */
	@Override
	public void init(IPageSite pageSite) {
		super.init(pageSite);
		IActionBars bars = pageSite.getActionBars();
		String id = ActionFactory.UNDO.getId();
		bars.setGlobalActionHandler(id, editorActionRegistry.getAction(id));
		id = ActionFactory.REDO.getId();
		bars.setGlobalActionHandler(id, editorActionRegistry.getAction(id));
		bars.updateActionBars();
	}

	/**
	 * 
	 * 
	 * @return the editorActionRegistry
	 */
	public ActionRegistry getEditorActionRegistry() {
		return editorActionRegistry;
	}

	/**
	 * 
	 * 
	 * @param editorActionRegistry the editorActionRegistry to set
	 */
	public void setEditorActionRegistry(ActionRegistry editorActionRegistry) {
		this.editorActionRegistry = editorActionRegistry;
	}

	/**
	 * 
	 * 
	 * @return the editDomain
	 */
	public EditDomain getEditDomain() {
		return editDomain;
	}

	/**
	 * toSet the editDomain in the treeOutlineOage.
	 * 
	 * @param editorDomain the editDomain to set
	 */
	public void setEditDomain(EditDomain editorDomain) {
		this.editDomain = editorDomain;
	}

//	/**
//	* 
//	* 
//	* @return the selectionSynchronizer
//	*/
//	public SelectionSynchronizer getSelectionSynchronizer() {
//	return selectionSynchronizer;
//	}

//	/**
//	* 
//	* 
//	* @param selectionSynchronizer the selectionSynchronizer to set
//	*/
//	public void setSelectionSynchronizer(SelectionSynchronizer selectionSynchronizer) {
//	this.selectionSynchronizer = selectionSynchronizer;
//	}

	/**
	 * 
	 * 
	 * @return the model
	 */
	public ModelManager getModel() {
		return modelmanager;
	}

	/**
	 * Tabbed properties.
	 * 
	 * @return 
	 */
	public String getContributorId() {
		return "TreeOutlinePage";
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	public Object getAdapter(Class adapter) {
		if (adapter == IPropertySheetPage.class) {
			if (tabbedPropertySheetPage == null) {
				tabbedPropertySheetPage = new TabbedPropertySheetPage(this);
			}
			return tabbedPropertySheetPage;
		} else if(ModelManager.class == adapter) {
			return modelmanager;
		}
		return Platform.getAdapterManager().getAdapter(this, adapter);
	}

	/**
	 * this method is implemented to change its visibility from protected to public.
	 * 
	 * @return 
	 */
	/*public EditPartViewer getViewer() {
		return super.getViewer();
	}*/


	/**
	 * Action to sort elements with a TypeSorter.
	 */
	public class SortAction extends Action {

		/** Sorter for the tree viewer */
		TypeSorter typeSorter = new TypeSorter();

		/**
		 * Creates a new SortAction
		 */
		public SortAction() {
			super();
			setText("Sort Elements");
			setImageDescriptor(PapyrusPlugin.getImageDescriptor(PapyrusPlugin.PAPYRUS_ICONS_16x16+"Sort.gif")); //$NON-NLS-1$
			setToolTipText("Sort elements in outline");
			setDescription("Description of the sorting action");

			boolean checked= PapyrusPlugin.getDefault().getPreferenceStore().getBoolean("SortingAction.isChecked"); //$NON-NLS-1$
			valueChanged(checked, false);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.action.Action#run()
		 */
		@Override
		public void run() {
			valueChanged(this.isChecked(), true);
		}

		/**
		 * method called when the action runs
		 * @param on boolean that indicates if the shower is used or not
		 * @param store boolean set to <code>true</code> if the preference should be saved in the preference store.
		 */
		private void valueChanged(final boolean on, boolean store) {
			// update the button...
			setChecked(on);

			// change the sorter and refresh the outline
			BusyIndicator.showWhile(getViewer().getControl().getDisplay(), new Runnable() {
				public void run() {

					// save previous selection
					ISelection selection = getViewer().getSelection();
					if (on) {
						// type sorter should be applied to the tree
						modelmanager.setOutlineSorter(typeSorter);
						getViewer().setContents(getViewer().getContents());

					} else {
						// no sorter should be applied
						modelmanager.setOutlineSorter(null);
						getViewer().setContents(getViewer().getContents());
					}
					// restore selection and set focus (otherwise, focus is given to the editor)
					getViewer().setSelection(selection);
				}
			});

			// save in preference store of the papyrus plugin
			if (store)
				PapyrusPlugin.getDefault().getPreferenceStore().setValue("SortingAction.isChecked", on); //$NON-NLS-1$
		}
	}

	/**
	 * Action to see only diagrams.
	 */
	public class DiagramViewAction extends Action {
		boolean diagramsview= false;
		EditPart modelEditPart=null;
		EditPart diagramrootEditPart=null;
		ISelection selection=null;
		/**
		 * Creates a new SortAction
		 */
		public DiagramViewAction() {
			super();
			setText("View diagrams");
			setImageDescriptor(PapyrusPlugin.getImageDescriptor(PapyrusPlugin.PAPYRUS_ICONS_16x16+"diagrams.gif")); //$NON-NLS-1$
			setToolTipText("View diagrams in outline");
			setDescription("Description of the diagrams list");
		}

		@Override
		public void run() {
			// update the button...
			BusyIndicator.showWhile(getViewer().getControl().getDisplay(), new Runnable() {
				public void run() {
					//context diagram view
					if (diagramsview) {
						//we want to see model view
						diagramrootEditPart=getViewer().getContents();
						if( modelEditPart==null){
							getViewer().setContents(getModel());
						}
						else{
							getViewer().setContents(modelEditPart);
						}
						//select the former element
						getViewer().setSelection(selection);
						diagramsview=false;
					} 
					// context model view
					else {
						//save selected element
						selection= getViewer().getSelection();
						//save root editpart
						modelEditPart=getViewer().getContents();
						if(diagramrootEditPart==null){
							getViewer().setContents(modelmanager.getDiResourceSet().getDiResource());}
						else{getViewer().setContents(diagramrootEditPart);}
						diagramsview=true;
					}
					setChecked(diagramsview);
				}
			});
		}
	}

	public class ShowInheritedElementAction extends Action {


		/**
		 * Creates a new ShowInheritedElementAction
		 */
		public ShowInheritedElementAction() {
			super();
			setText("Show inherited element");
			setImageDescriptor(PapyrusPlugin.getImageDescriptor(
					PapyrusPlugin.PAPYRUS_ICONS_16x16+"Inherited.gif")); //$NON-NLS-1$
			setToolTipText("Show inherited element");
			setDescription("Show inherited element");

			boolean checked=PapyrusPlugin.getDefault().getPreferenceStore().getBoolean(
					IPapyrusUIConstants.INHERITED_CONSTANT);
			valueChanged(checked, false);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void run() {
			valueChanged(this.isChecked(), true);
		}

		/**
		 * method called when the action runs
		 * @param on boolean that indicates if the shower is used or not
		 * @param store boolean set to <code>true</code> if the preference should be saved in the preference store.
		 */
		private void valueChanged(final boolean on, boolean store) {
			// update the button...
			setChecked(on);
			// save in preference store of the papyrus plugin
			if (store){
				PapyrusPlugin.getDefault().getPreferenceStore().setValue(IPapyrusUIConstants.INHERITED_CONSTANT, on); //$NON-NLS-1$
			}
			// change the sorter and refresh the outline
			BusyIndicator.showWhile(getViewer().getControl().getDisplay(), new Runnable() {
				public void run() {

					// save previous selection
					ISelection selection = getViewer().getSelection();
					getViewer().setContents(getViewer().getContents());

					// restore selection and set focus (otherwise, focus is given to the editor)
					getViewer().setSelection(selection);
				}
			});



		}			



	}
}
